﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Collections;

namespace DynamicCompiler
{
    public class Program
    {
        static void Main(string[] args)
        {
            if (args.Length > 2)
            {
                List<string> files = new List<string>();
                for (int i = 2; i < args.Length; i++)
                {
                    files.Add(args[i]);
                }
                Link(args[0], files.ToArray(), GenerateHeader(files.ToArray(), new Version(args[1])));
                Console.WriteLine("Files linked.");
                Console.WriteLine("Output file: " + args[0]);
            }
            else
            {
                Console.WriteLine("USAGE: output.out version file1.easm file2.easm file3.easm file4.easm ...");
            }
        }
        public static string GenerateHeader(string[] codeFiles, Version appVersion)
        {
            string header = "";
            header += "HEADER::START\n"; // Start the header
            header += "EASM_VER::1.0.0.0\n"; // Add the EASM version
            header += "APP_VER::" + appVersion.ToString(4) + "\n"; // Ad the APP version
            header += "FILES::START\n"; // Start Files Addressing
            int findex = 0;
            foreach (string codeFile in codeFiles)
            {
                FileInfo fi = new FileInfo(codeFile);
                header += string.Format("{0}::{1}::{2}\n", findex, fi.Name, fi.Length);
                findex++;
            }
            header += "FILES::END\n"; // End Files Addressing
            header += "HEADER::END\n"; // End the header
            return header; // Return the header
        }
        public static void Link(string output, string[] codeFiles, string header)
        {
            if(File.Exists(output)) File.Delete(output);
            string finalFile = header;
            foreach (string codeFile in codeFiles)
            {
                if (File.Exists(codeFile))
                {
                    FileStream fs = null;
                    StreamReader sr = null;
                    try
                    {
                        fs = new FileStream(codeFile, FileMode.Open);
                        sr = new StreamReader(fs);
                        finalFile += sr.ReadToEnd();
                        sr.Close();
                        fs.Close();
                    }
                    catch
                    {
                        if (sr != null)
                        {
                            sr.Close();
                            sr = null;
                        }
                        if (fs != null)
                        {
                            fs.Close();
                            sr = null;
                        }
                    }
                }
            }
            FileStream ofs = new FileStream(output, FileMode.OpenOrCreate);
            GZipStream ogz = new GZipStream(ofs, CompressionMode.Compress);
            StreamWriter osw = new StreamWriter(ogz);
            osw.Write(finalFile);
            osw.Close();
            ogz.Close();
            ofs.Close();
        }
        public static LinkedFile[] GetFiles(string compressedFile)
        {
            List<LinkedFile> ret = new List<LinkedFile>();
            if (File.Exists(compressedFile))
            {
                FileStream fs = new FileStream(compressedFile, FileMode.Open);
                GZipStream gz = new GZipStream(fs, CompressionMode.Decompress);
                StreamReader sr = new StreamReader(gz);
                string cFile = sr.ReadToEnd();
                sr.Close();
                gz.Close();
                fs.Close();
                bool isReading = true;
                bool isFiles = false;
                int len = 0;
                foreach (string s in cFile.Split('\n'))
                {
                    if (s == "HEADER::END") isReading = false;
                    else if (s == "FILES::START") isFiles = true;
                    else if (s == "FILES::END") isFiles = false;
                    if (isReading)
                    {
                        if (isFiles && s != "FILES::START")
                        {
                            string[] file = s.Split(new string[] { "::" }, StringSplitOptions.None);
                            LinkedFile lf = new LinkedFile();
                            lf.FileIndex = int.Parse(file[0]);
                            lf.Start = len;
                            lf.Length = int.Parse(file[2]);
                            lf.Name = file[1];
                            len += int.Parse(file[2]);
                            ret.Add(lf);
                        }
                    }
                }
            }
            return ret.ToArray();
        }
        public static string GetFile(string compressedFile, int index)
        {
            string file = "";
            FileStream fs = null;
            StreamReader sr = null;
            try
            {
                LinkedFile[] cont = GetFiles(compressedFile);
                fs = new FileStream(compressedFile, FileMode.Open);
                GZipStream gz = new GZipStream(fs, CompressionMode.Decompress);
                sr = new StreamReader(gz);
                string header = "";
                bool head = true;
                string full = sr.ReadToEnd();
                foreach (string line in full.Split('\n'))
                {
                    if (head)
                    {
                        header += line + "\n";
                        if (line == "HEADER::END") head = false;
                    }
                    else
                    {
                        break;
                    }
                }
                for (int i = header.Length; i < full.Length; i++)
                {
                    file += full[i];
                }
            }
            catch (Exception ex) { Console.WriteLine(ex.Message); }
            finally
            {
                if (sr != null)
                {
                    sr.Close();
                    sr = null;
                }
                if (fs != null)
                {
                    fs.Close();
                    fs = null;
                }
            }
            return file;
        }
    } 
    public struct LinkedFile
    {
        public int FileIndex, Start, Length;
        public string Name;
    }
}
